// 多重背包单调队列优化
// 宝物筛选
// 一共有n种货物, 背包容量为t
// 每种货物的价值(v[i])、重量(w[i])、数量(c[i])都给出
// 请返回选择货物不超过背包容量的情况下，能得到的最大的价值
// 测试链接 : https://www.luogu.com.cn/problem/P1776
// 提交以下的code，可以直接通过

#include <bits/stdc++.h>

using namespace std;

const int MAXN = 101;
const int MAXW = 40001;
int v[MAXN], w[MAXN], c[MAXN];
int dp[MAXW];
int q[MAXW], l, r;
int n, t;

// s的容量用来装i号商品，可以得到多少价值
int inc(int s, int i)
{
    return s / w[i] * v[i];
}

// 严格位置依赖的动态规划 + 单调队列优化枚举
// 单调队列优化枚举本质就是通过滑动窗口来维护
// 某个最大值，使用这个最大值时，直接从窗口中
// 获取即可，而不需要通过枚举来获得
int compute1()
{
    int dp[n + 1][t + 1];
    memset(dp, 0, sizeof dp);
    for(int i = 1; i <= n; ++i)
    {
        // 根据余数 mod 来分组
        for(int mod = 0; mod <= min(t, w[i] - 1); ++mod)
        {
            l = r = 0;
            for(int j = mod; j <= t; j += w[i]) // 同余的为一组
            {
                // dp[i-1][j]
                // dp[i][j]
                // q[r - 1] -> x
                // j -> y
                // dp[i - 1][j] 和 dp[i - 1][q[r - 1]] 都是不使用i号物品所获得的
                // 最大价值，但它们的最大容量限制不相同，因此需要使用i号物品来填上这
                // 个容量的差值，如果补充容量差值所带来的价值，都不比 dp[i - 1][j]
                // 大的话，就可以淘汰这个指标了
                while(l < r && dp[i - 1][q[r - 1]] + inc(j - q[r - 1], i) <= dp[i - 1][j])
                {
                    // q[r-1]是队列尾部的列号 vs j这个列号
                    // dp[i-1][q[r-1]] 是从前i-1个物品中选，容量不超过q[r-1]的最大价值
                    // inc(j - q[r - 1], i)为补齐容量差值后所获得的价值，如果补齐后没有
                    // dp[i-1][j]大，那么同一组后续的值就不可能由dp[i - 1][q[r - 1]] 
                    // + inc(j - q[r - 1], i)来更新了，那么就让它出窗口
                    // 指标之间pk
                    --r;
                }
                q[r++] = j;
                if(q[l] + w[i] * (c[i] + 1) == j)
                {
                    // 检查单调队列最左的列号，是否过期
                    // 比如
                    // i号物品，重量为3，个数4
                    // q[l]是队列头部的列号，假设是2
                    // 当j == 17时，依赖的格子为dp[i-1][17、14、11、8、5]
                    // 17, 不用i号物品
                    // 14, 用1个i号物品
                    // ...
                    // 5, 用4个i号物品 
                    // 所以此时头部的列号2，过期了，要弹出
                    ++l;
                }
                // dp[i][j] = dp[i-1][拥有最强指标的列] + (j - 拥有最强指标的列) / i号物品重量 * i号物品价值
                dp[i][j] = dp[i - 1][q[l]] + inc(j - q[l], i);
            }
        }
    }
    return dp[n][t];
}

// 单调队列优化枚举 + 空间压缩
// 理解了原理之后，这个函数就没有理解难度了
// 难度来自实现和注意边界条件，可以自己尝试一下
int compute2()
{
    for(int i = 1; i <= n; ++i)
    {
        for(int mod = 0; mod <= min(t, w[i] - 1); ++mod)
        {
            // 因为空间压缩时，关于j的遍历是从右往左，而不是从左往右
            // 所以先让dp[i-1][...右侧的几个位置]进入窗口
            l = r = 0;
            for(int j = t - mod, k = 0; j >= 0 && k <= c[i]; j -= w[i], ++k)
            {
                while(l < r && dp[j] + inc(q[r - 1] - j, i) >= dp[q[r - 1]])
                {
                    --r;
                }
                q[r++] = j;
            }
            // 然后j开始从右往左遍历
            // 注意，因为j从右往左遍历，所以：
            // 更靠右的j位置更早进入窗口
            // 更靠左的j位置更晚进入窗口
            // 由于上面的 for 循环有 k <= c[i] 的限制
            // 所以一开始窗口内的值都是没有过期的
            for(int j = t - mod; j >= 0; j -= w[i])
            {
                // 来到j，计算dp[i][j]的值，做了空间压缩，所以去更新dp[j]
                dp[j] = dp[q[l]] + inc(j - q[l], i);
                // 求解完dp[j]
                // 接下来要去求解dp[j-w[i]]了(根据余数分组)
                // 所以看看窗口最左的下标是不是j(其实代表dp[i-1][j]的值]
                // 是的话，说明最左下标过期了，要弹出
                if(q[l] == j) ++l;
                // 最后
                // 因为接下来要去求解dp[j-w[i]]了
                // 所以新的dp[i-1][enter]要进入窗口了
                // 用单调队列的更新方式让其进入
                int enter = j - w[i] * (c[i] + 1);
                if(enter >= 0)
                {
                    while(l < r && dp[enter] + inc(q[r - 1] - enter, i) >= dp[q[r - 1]])
                    {
                        --r;
                    }
                    q[r++] = enter;
                }
            }
        }
    }
    return dp[t];
}

int main()
{
    scanf("%d%d", &n, &t);
    for(int i = 1; i <= n; ++i)
    {
        scanf("%d%d%d", &v[i], &w[i], &c[i]);
    }
    printf("%d\n", compute2());

    return 0;
}